# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <assert.h>
# include "list.h"
# include "tuple.h"

/* Estructura de un Nodo */
struct sNode
{
    /** Puntero a otro Nodo (celda) */
    celda next;
    /** Tupla (Nodo, Capacidad) */
    Tuple tup;
};

/* Estructura de una Lista de Nodos */
struct sList
{
    /** Puntero a la primer Tupla de la Lista */
    celda first;
    /** Puntero a la última Tupla de la Lista */
    celda last;
    /** Tamaño de la Lista */
    int size;
};

/** Crea una lista */
List list_create (void){
    List newList = NULL;
    
    newList = (List) malloc (sizeof (struct sList));
    if (newList != NULL){
        newList->first = NULL;
        newList->last = NULL;
        newList->size = 0;
    }
    
    return newList;
}

/** Verifica si la lista es vacia */
bool list_is_empty (List l){
    bool b = false;

    assert (l);

    if(l->size == 0) b = true;

    return b;
}

/** Verifica si el elemnto pertenece a la lista */
bool list_exists_elem (List l, elem e){
    celda currentNode;
    bool exists = false;

    assert (l);
    assert (e);

    if (!list_is_empty (l)){
        currentNode = l->first;
	/* exists = tuple_compare (e, currentNode->data); */

        while((!exists) && (currentNode != NULL)){
            exists = tuple_compare (e, currentNode->tup);
            currentNode = currentNode->next;
        }
    }

    return exists;
}

/** Agrega un elemento a la lista */
List list_add_new_elem (List l, elem e){
    celda newNode; 
    
    assert (l);
    assert (e);

    newNode = (celda) malloc (sizeof (struct sNode));
    if(newNode != NULL){
        newNode->tup = tuple_clone(e);
	newNode->next = NULL;

        if (l->first == NULL) {
            l->first = newNode;
	    l->last = newNode;
	    l->size = 1;
        } else {
	    l->last->next = newNode;
	    l->last = newNode;
	    l->size ++;
	}
    }
    return l;
}

/** Obtiene el tamaño de una lista */
int list_get_size (List l){
    assert(l);
    return l->size;
}

/** Elimina un elemento de la lista */
List list_destroy_elem (List l, elem e){
    celda currentNode = NULL, p = NULL;
    /* bool exists = false; */

    assert (l);
    assert (e);

    if ((!list_is_empty (l)) && (list_exists_elem(l,e))){
        currentNode = l->first;
      
        if (tuple_compare (e, currentNode->tup)){
	    l->first = l->first->next;
	    currentNode->next = NULL;
	    currentNode->tup = tuple_destroy (currentNode->tup);
	    free (currentNode);
	} else {
	    while (!tuple_compare (e,currentNode->next->tup)){
	        currentNode = currentNode->next;
	    }
	    
	    p = currentNode->next;
	    currentNode->next = currentNode->next->next;
	    p->next = NULL;
	    p->tup = tuple_destroy (p->tup);
	    free (p);
	}
	
	l->size = l->size-1;
    }

    return l;
}

/* Elimina un elemento de la lista segun su posicion 
List list_destroy_elem_by_p (List l, int position){
   Node * currentNode = NULL;
   
   assert (l);
   
   if(list_get_size(l) >= position ){
       l->first = currentNode;
       
   }
   
   return l;
}*/

/** Devuelve un elemento de la lista segun su posicion  */
elem list_get_elem (List l, long int position){
   elem e = NULL;
   celda c = NULL;
   long int i=1;
   
   if(position<=list_get_size(l)){
       c = l->first;
       
       while(i<=(position-1)){
	   c = c->next;
	   i++;
       }
       
       e = tuple_clone (c->tup);
   }

    return e;
}

/** Destruye una lista */
void list_destroy (List l){
   celda p=NULL;
   
   assert (l);
   
   if(!list_is_empty (l)){
       l->last = NULL;
       
       while (l->size != 0){
	   p = l->first;
	   l->first = l->first->next;
	   p->next = NULL;
	   
	   p->tup = tuple_destroy (p->tup);
	   free(p);
	   
	   l->size = l->size -1;
       }
   }
   
   free(l);
}

